#region Using Statements
using System;
using GameStateManagement;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Input.Touch;
using System.Collections.Generic;
#endregion

namespace Life
{
    // TODO: Use PhoneApplicationService for options, instead of a file?

    /// <summary>
    /// This screen implements the actual game logic. It is just a
    /// placeholder to get the idea across: you'll probably want to
    /// put some more interesting gameplay in here!
    /// </summary>
    class GameplayScreen : GameScreen
    {
        #region Fields

        ContentManager content;

        IGameManager gameManager;

        float pauseAlpha;

        InputAction pauseAction;

        Texture2D deadTexture;
        Texture2D liveTexture;
        Texture2D cellTexture;

        Texture2D playButtonTexture;
        Texture2D pauseButtonTexture;
        Texture2D slowerButtonTexture;
        Texture2D fasterButtonTexture;
        Texture2D clearButtonTexture;
        Texture2D randomButtonTexture;
        Texture2D saveButtonTexture;
        Texture2D loadButtonTexture;
        Texture2D helpButtonTexture;

        // TODO: Centralize gameplay menu configuration somewhere reasonable
        const int menuButtonCount = 7;        
        int menuButtonWidth;

        int boardSquareWidth;
        int xOffset;
        int yOffset;
        int yMenuOffset;

        private CellView[,] _cells;

        #endregion

        #region Initialization


        /// <summary>
        /// Constructor.
        /// </summary>
        public GameplayScreen()
        {
            TransitionOnTime = TimeSpan.FromSeconds(.3);
            TransitionOffTime = TimeSpan.FromSeconds(0.2);

            pauseAction = new InputAction(
                new Buttons[] { Buttons.Start, Buttons.Back },
                new Keys[] { Keys.Escape },
                true);

            EnabledGestures = GestureType.Tap;

            gameManager = null;
            _cells = null;
        }

        /// <summary>
        /// Load graphics content for the game.
        /// </summary>
        public override void Activate(bool instancePreserved)
        {
            if (!instancePreserved)
            {
                if (content == null)
                    content = new ContentManager(ScreenManager.Game.Services, "Content");

                if (gameManager == null)
                {
                    gameManager = (IGameManager)ScreenManager.Game.Services.GetService(typeof(IGameManager));
                    gameManager.Initialize();
                }

                deadTexture = content.Load<Texture2D>("dead");
                liveTexture = content.Load<Texture2D>("live");
                cellTexture = content.Load<Texture2D>("cellStrip");

                playButtonTexture = content.Load<Texture2D>("playButton");
                pauseButtonTexture = content.Load<Texture2D>("pauseButton");
                slowerButtonTexture = content.Load<Texture2D>("slowerButton");
                fasterButtonTexture = content.Load<Texture2D>("fasterButton");
                clearButtonTexture = content.Load<Texture2D>("clearButton");
                randomButtonTexture = content.Load<Texture2D>("randomButton");
                saveButtonTexture = content.Load<Texture2D>("saveButton");
                loadButtonTexture = content.Load<Texture2D>("loadButton");
                helpButtonTexture = content.Load<Texture2D>("helpButton");

                // TODO: consolidate overlap with background scene

                int horizontalCount = gameManager.BoardWidth;
                int verticalCount = gameManager.BoardHeight;

                // Calculate menu size and available board space

                int availableWidth = ScreenManager.GraphicsDevice.Viewport.Width;
                menuButtonWidth = availableWidth / menuButtonCount;
                int availableHeight = ScreenManager.GraphicsDevice.Viewport.Height - menuButtonWidth;

                // Calculate grid size

                int proposedHorizontalWidth = availableWidth / horizontalCount;
                int proposedVerticalWidth = availableHeight / verticalCount;

                if (proposedHorizontalWidth > proposedVerticalWidth)
                    boardSquareWidth = proposedVerticalWidth;
                else
                    boardSquareWidth = proposedHorizontalWidth;

                xOffset = (availableWidth - boardSquareWidth * horizontalCount) / 2;
                yOffset = (availableHeight - boardSquareWidth * verticalCount) / 2;
                yMenuOffset = yOffset + boardSquareWidth * verticalCount;

                if (_cells == null)
                {
                    _cells = new CellView[gameManager.BoardWidth, gameManager.BoardHeight];
                }

                // Fill in grid with new cells
                int x = xOffset;
                int y = yOffset;
                for (int i = 0; i < gameManager.BoardWidth; i++)
                {
                    y = yOffset;

                    for (int j = 0; j < gameManager.BoardHeight; j++)
                    {
                        _cells[i, j] = new CellView(cellTexture, new Vector2(x, y), boardSquareWidth, boardSquareWidth, 
                            cellTexture.Width / 100, 50, Color.White, 1, gameManager.IsLive(i, j));

                        y += boardSquareWidth;
                    }

                    x += boardSquareWidth;
                }

                // once the load has finished, we use ResetElapsedTime to tell the game's
                // timing mechanism that we have just finished a very long frame, and that
                // it should not try to catch up.
                ScreenManager.Game.ResetElapsedTime();
            }
        }

        public override void Deactivate()
        {
            gameManager.Stop();

            base.Deactivate();
        }

        /// <summary>
        /// Unload graphics content used by the game.
        /// </summary>
        public override void Unload()
        {
            gameManager.Stop();

            content.Unload();
        }


        #endregion

        #region Update and Draw

        /// <summary>
        /// Updates the state of the game. This method checks the GameScreen.IsActive
        /// property, so the game will stop updating when the pause menu is active,
        /// or if you tab away to a different application.
        /// </summary>
        public override void Update(GameTime gameTime, bool otherScreenHasFocus, bool coveredByOtherScreen)
        {
            base.Update(gameTime, otherScreenHasFocus, false);

            if (!coveredByOtherScreen && !otherScreenHasFocus)
            {
                // Update cells
                for (int i = 0; i < gameManager.BoardWidth; i++)
                {
                    for (int j = 0; j < gameManager.BoardHeight; j++)
                    {
                        _cells[i, j].Update(gameTime, gameManager.IsLive(i, j));
                    }
                }
            }

            // Gradually fade in or out depending on whether we are covered by the pause screen.
            if (coveredByOtherScreen)
                pauseAlpha = Math.Min(pauseAlpha + 1f / 32, 1);
            else
                pauseAlpha = Math.Max(pauseAlpha - 1f / 32, 0);
        }

        /// <summary>
        /// Lets the game respond to player input. Unlike the Update method,
        /// this will only be called when the gameplay screen is active.
        /// </summary>
        public override void HandleInput(GameTime gameTime, InputState input)
        {
            if (input == null)
                throw new ArgumentNullException("input");

            PlayerIndex player;
            if (pauseAction.Evaluate(input, ControllingPlayer, out player))
            {
                LoadingScreen.Load(ScreenManager, false, player, new BackgroundScreen(),
                    new MainMenuScreen());
            }
            else
            {
                // TODO: Consolidate gameplay menu handling to a separate class/file.  This is messy.

                if (input.TouchState.Count > 0)
                {
                    if (input.TouchState[0].State == TouchLocationState.Pressed)
                    {
                        const int generationIncrement = 200;

                        Vector2 touchPosition = input.TouchState[0].Position;

                        int x = (int)((touchPosition.X - xOffset) / boardSquareWidth);
                        int y = (int)((touchPosition.Y - yOffset)/ boardSquareWidth);

                        if (y < gameManager.BoardHeight)
                        {
                            // Prevent cell animation on user tap
                            _cells[x, y].Update(!gameManager.IsLive(x, y));

                            gameManager.ToggleCell(x, y);
                        }
                        else if (touchPosition.X <= menuButtonWidth * 1) // play/paused button
                        {
                            if (gameManager.IsPaused)
                                gameManager.Start();
                            else
                                gameManager.Stop();
                        }
                        else if (touchPosition.X <= menuButtonWidth * 2) // slower button
                        {
                            gameManager.GenerationIntervalMilliseconds += generationIncrement;
                        }
                        else if (touchPosition.X <= menuButtonWidth * 3) // faster button
                        {
                            gameManager.GenerationIntervalMilliseconds -= generationIncrement;
                        }
                        else if (touchPosition.X <= menuButtonWidth * 4) // clear/random button 
                        {
                            if (gameManager.IsClear)
                                gameManager.Randomize();
                            else
                                gameManager.Clear();
                        }
                        else if (touchPosition.X <= menuButtonWidth * 5) // save button
                        {
                            LoadingScreen.Load(ScreenManager, false, null, new SaveScreen());
                        }
                        else if (touchPosition.X <= menuButtonWidth * 6) // load button
                        {
                            LoadingScreen.Load(ScreenManager, false, null, new LoadScreen(false));
                        }
                        else if (touchPosition.X <= menuButtonWidth * 7) // help button
                        {
                            LoadingScreen.Load(ScreenManager, false, null, new HelpScreen(false));
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Draws the gameplay screen.
        /// </summary>
        public override void Draw(GameTime gameTime)
        {
            ScreenManager.GraphicsDevice.Clear(ClearOptions.Target, Color.Black, 0, 0);

            SpriteBatch spriteBatch = ScreenManager.SpriteBatch;

            spriteBatch.Begin();

            // TODO: Consolidate with background scene?

            for (int i = 0; i < gameManager.BoardWidth; i++)
            {
                for (int j = 0; j < gameManager.BoardHeight; j++)
                {
                    _cells[i, j].Draw(spriteBatch);
                }
            }

            // Draw menu
            // TODO: Consolidate gameplay menu handling into separate class
            if (ScreenState != ScreenState.Hidden)
            {
                int spacing = 10;
                Rectangle r = new Rectangle(spacing, yMenuOffset + spacing, menuButtonWidth - spacing * 2, menuButtonWidth - spacing * 2);

                if (gameManager.IsPaused)
                {
                    spriteBatch.Draw(playButtonTexture, r, Color.White);
                }
                else
                {
                    spriteBatch.Draw(pauseButtonTexture, r, Color.White);
                }

                r.X += menuButtonWidth;
                spriteBatch.Draw(slowerButtonTexture, r, Color.White);

                r.X += menuButtonWidth;
                spriteBatch.Draw(fasterButtonTexture, r, Color.White);

                r.X += menuButtonWidth;
                if (gameManager.IsClear)
                {
                    spriteBatch.Draw(randomButtonTexture, r, Color.White);
                }
                else
                {
                    spriteBatch.Draw(clearButtonTexture, r, Color.White);
                }

                r.X += menuButtonWidth;
                spriteBatch.Draw(saveButtonTexture, r, Color.White);

                r.X += menuButtonWidth;
                spriteBatch.Draw(loadButtonTexture, r, Color.White);

                r.X += menuButtonWidth;
                spriteBatch.Draw(helpButtonTexture, r, Color.White);

                // If the game is transitioning on or off, fade it out to black.
                if (TransitionPosition > 0 || pauseAlpha > 0)
                {
                    float alpha = MathHelper.Lerp(1f - TransitionAlpha, 1f, pauseAlpha / 2);

                    ScreenManager.FadeBackBufferToBlack(alpha);
                }
            }

            // Draw generation count in upper left corner
            spriteBatch.DrawString(ScreenManager.Font, gameManager.Generation.ToString(), new Vector2(xOffset + 10, yOffset + 10), Color.DarkRed);            

            spriteBatch.End();
        }
        #endregion
    }
}
